package com.servinf.ani.sincronizador;

import com.servinf.ani.comparator.AssignmentComparator;
import com.servinf.ani.comparator.LaneComparator;
import com.servinf.ani.comparator.ProjectComparator;
import com.servinf.ani.sincronizador.entities.Concesionaria;
import com.servinf.ani.sincronizador.entities.Departamento;
import com.servinf.ani.sincronizador.entities.EstadoIniciativa;
import com.servinf.ani.sincronizador.entities.EstadoIntervencion;
import com.servinf.ani.sincronizador.entities.Intervencion;
import com.servinf.ani.sincronizador.entities.Proyecto;
import com.servinf.ani.sincronizador.entities.SubEstadosParaProyectos;
import com.servinf.ani.sincronizador.entities.TipoInicitativa;
import com.servinf.ani.sincronizador.entities.TipoIntervencion;
import com.servinf.ani.sincronizador.entities.TipoProyecto;
import com.servinf.ani.sincronizador.entities.TramoVia;
import com.servinf.ani.sincronizador.exception.ExcelReaderException;
import com.servinf.ani.sincronizador.exception.IncompatibleTypesException;
import com.servinf.ani.sincronizador.reader.ExcelReader;
import com.servinf.ani.sincronizador.service.AssignmentService;
import com.servinf.ani.sincronizador.service.AssignmentStateService;
import com.servinf.ani.sincronizador.service.AssignmentTypeService;
import com.servinf.ani.sincronizador.service.ConcessionService;
import com.servinf.ani.sincronizador.service.DepartmentService;
import com.servinf.ani.sincronizador.service.InitiativeStateService;
import com.servinf.ani.sincronizador.service.InitiativeTypeService;
import com.servinf.ani.sincronizador.service.LaneService;
import com.servinf.ani.sincronizador.service.ProjectService;
import com.servinf.ani.sincronizador.service.ProjectTypeService;
import com.servinf.ani.sincronizador.service.StateByProjectService;
import com.servinf.ani.sincronizador.utils.FTPHelper;
import com.servinf.ani.sincronizador.utils.LogHelper;
import com.servinf.ani.sincronizador.utils.PropertiesHelper;
import com.servinf.ani.sincronizador.utils.RelationMaps;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.CannotCreateTransactionException;


/**
 * Clase principal por la que inicia la ejecución del sincronizador y la que maneja el flujo principal
 * de la aplicación.
 * @author David
 */
@Configuration
@EnableAutoConfiguration
public class App    {
    
    private static String excelPath;
    private static String dateFormatString;

    private static ExcelReader excelReader;
    public static ApplicationContext context;

    public static int maxId;  //Id en tramos no es serial por eso se debe saber cual es el maximo id en la tabla
    public static Map<String, String> concessionsMap;
    
    public static void main( String[] args ) throws ParseException, IOException    {
        try  {
            LogHelper.setup();
            LogHelper.Log(LogHelper.INFO, "************** INICIA SINCRONIZACIÓN **************", null);
            context = new ClassPathXmlApplicationContext("spring-config.xml");
            dateFormatString = PropertiesHelper.getInstance().getProperties().getProperty("format.dateFormat");
            
            excelPath = FTPHelper.downloadFileFromFTP(); //Ruta del archivo de excel.
            //excelPath = "C:\\ani_files\\sincronizador\\excel\\Tabla Sincronizacion Project GEO.xlsx";
            if(excelPath != null) {
                excelReader = new ExcelReader();
            
                BackUp_RestoreDB.backUp();

                synchronizeProjects(); // Se sincronizan los proyectos
                synchronizeLanes(); //Se sincronizan los tramos.
                synchronizeAssignments(); // Se sincronizan las intervenciones(Asignaciones en el archivo excel)
                LogHelper.Log(LogHelper.INFO, "************** FINALIZA SINCRONIZACIÓN **************", null);
            }else {
                LogHelper.Log(LogHelper.ERROR, "No se pudo descargar el archivo", null);
            }
            
        }catch (IOException e) { //Excepción en los Logs
            e.printStackTrace();
        }catch(CannotCreateTransactionException conectionException) {
            LogHelper.Log(LogHelper.ERROR, "Problema al conectar con la base de datos", conectionException);
        }catch(Exception e) {
            LogHelper.Log(LogHelper.ERROR, "Ha ocurrido un problema en la sincronización", e);
            e.printStackTrace();
        }
        
    }

    /**
     * Método que almacena la información necesaria para la sincronización
     */
    private static void collectInformationForProjects() throws IOException {
        
        LogHelper.Log(LogHelper.INFO, "Inicia la recopilación de información existente en BD para la creación de proyectos", null);
        //Tipos de proyecto (Generación)
        LogHelper.Log(LogHelper.INFO, "Tipos de proyecto (Generación) ", null);
        ProjectTypeService projectTypeService = (ProjectTypeService) context.getBean("projectTypeService");
        List<TipoProyecto> types = projectTypeService.findALl();
        RelationMaps.getInstance().setProjectTypesMap(types);
        LogHelper.Log(LogHelper.INFO, RelationMaps.getInstance().getProjectTypesMap().toString(), null);
        
        //Tipos de iniciativa (Privada, pública)
        LogHelper.Log(LogHelper.INFO, "Tipos de iniciativa (Privada/Pública) ", null);
        InitiativeTypeService initiativeService = (InitiativeTypeService) context.getBean("initiativeTypeService");
        List<TipoInicitativa> initiativeTypes = initiativeService.findALl();
        RelationMaps.getInstance().setInitiativeTypeMap(initiativeTypes);
        LogHelper.Log(LogHelper.INFO, RelationMaps.getInstance().getInitiativeTypeMap().toString(), null);
        
        //Estados de la iniciativa (Factibilidad)
        LogHelper.Log(LogHelper.INFO, "Estados de iniciativa (Factibilidad) ", null);
        InitiativeStateService initiativeStateService = (InitiativeStateService) context.getBean("initiativeStateService");
        List<EstadoIniciativa> initiativeStates = initiativeStateService.findALl();
        RelationMaps.getInstance().setInitiativeStateMap(initiativeStates);
        LogHelper.Log(LogHelper.INFO, RelationMaps.getInstance().getInitiativeStateMap().toString(), null);
        
        //Estados de los proyectos
        LogHelper.Log(LogHelper.INFO, "Estados de proyecto ", null);
        StateByProjectService stateService = (StateByProjectService) context.getBean("stateByProjectService");
        List<SubEstadosParaProyectos> states = stateService.findALl();
        RelationMaps.getInstance().setStatesMap(states);
        LogHelper.Log(LogHelper.INFO, RelationMaps.getInstance().getStatesMap().toString(), null);
        
        //Departamentos...
        LogHelper.Log(LogHelper.INFO, "Departamentos ", null);
        DepartmentService departmentService = (DepartmentService) context.getBean("departmentService");
        List<Departamento> departamentList = departmentService.findAll();
        RelationMaps.getInstance().setDepartmentMap(departamentList);
        LogHelper.Log(LogHelper.INFO, RelationMaps.getInstance().getDepartmentMap().toString(), null);
        LogHelper.Log(LogHelper.INFO, "Finaliza la recopilación de información existente en BD", null);
    }
    
    /**
     * Método para manejar la sincronización de proyectos
     */
    private static void synchronizeProjects() throws IOException {
        collectInformationForProjects();
        LogHelper.Log(LogHelper.INFO, "Inicia sincronización de proyectos", null);
        ProjectService projectService = (ProjectService) context.getBean("projectService");
        try {
            List<List<Object>> projectExcelInfo = excelReader.read(excelPath, ExcelReader.PROJECT_SHEET_INDEX);
            concessionsMap = readConcessions();
            LogHelper.Log(LogHelper.INFO, "Buscando proyectos existentes en BD", null);
            List<Proyecto> projectsInDB = projectService.findALl();
            ProjectComparator.compareList(projectExcelInfo, projectsInDB);
            LogHelper.Log(LogHelper.INFO, "Finaliza sincronización de proyectos", null);
            
        }catch(FileNotFoundException fne) {
            String path = PropertiesHelper.getInstance().getProperties().getProperty("excel.filePath");
            LogHelper.Log(LogHelper.ERROR, "Error encontrando el archivo excel en la ruta " + path, fne);
        }catch(ExcelReaderException excelReaderException) {
            LogHelper.Log(LogHelper.ERROR, "Error en la lectura del archivo excel ", excelReaderException);
        }
    }
    
    /**
     * Método que se encarga de la sincronización de tramo vias.
     */
    private static void synchronizeLanes() throws IOException {
        LogHelper.Log(LogHelper.INFO, "Inicia sincronización de tramos", null);
        LaneService laneService = (LaneService) context.getBean("laneService");
        try {
            List<List<Object>> assignementExcelInfo = excelReader.read(excelPath, ExcelReader.ASSIGNMENT_SHEET_INDEX);
            LogHelper.Log(LogHelper.INFO, "Buscando tramos existentes en BD", null);
            List<TramoVia> lanesInBD = laneService.findALl();
            maxId = findMaxId(lanesInBD); //Máximo id en la tabla de tramos porque la tabla no tiene id serial
            RelationMaps.setLanesInExcel(assignementExcelInfo);
            LaneComparator.compareList(RelationMaps.getInstance().getLanesInExcel(), lanesInBD);
            LogHelper.Log(LogHelper.INFO, "Finaliza sincronización de tramos", null);
        }catch(FileNotFoundException fne) {
            String path = PropertiesHelper.getInstance().getProperties().getProperty("excel.filePath");
            LogHelper.Log(LogHelper.ERROR, "Error encontrando el archivo excel en la ruta " + path, fne);
        }catch(ExcelReaderException excelReaderException) {
            LogHelper.Log(LogHelper.ERROR, "Error en la lectura del archivo excel ", excelReaderException);
        }
    }
    
    /**
     * Reùne informaciòn necesaria para la creación de intervenciones
     */
    private static void collectInformationForAssignemnts() throws IOException {
        
        LogHelper.Log(LogHelper.INFO, "Inicia la recopilación de información existente en BD para la creación de intervenciones", null);
        //Tramos
        LogHelper.Log(LogHelper.INFO, "Tramos ", null);
        LaneService laneService = (LaneService) context.getBean("laneService");
        List<TramoVia> lanes = laneService.findALl();
        RelationMaps.getInstance().setLanesMap(lanes);
        LogHelper.Log(LogHelper.INFO, "Tramos actualizados guardados en memoria ", null);
               
        //Estados de intervención
        LogHelper.Log(LogHelper.INFO, "Estados de Intervención ", null);
        AssignmentTypeService assignmentTypeService = (AssignmentTypeService) context.getBean("assignmentTypeService");
        List<TipoIntervencion> types = assignmentTypeService.findALl();
        RelationMaps.getInstance().setAssignmentTypeMap(types);
        
        
        //Tipos de intervención
        LogHelper.Log(LogHelper.INFO, "Tipos de Intervención ", null);
        AssignmentStateService assignmentStateService = (AssignmentStateService) context.getBean("assignmentStateService");
        List<EstadoIntervencion> states = assignmentStateService.findALl();
        RelationMaps.getInstance().setAssignmentStateMap(states);
    }

    
    
    /**
     * Método para iniciar la actualización / creación de intervenciones.
     */
    private static void synchronizeAssignments() throws IOException {
        collectInformationForAssignemnts();
        LogHelper.Log(LogHelper.INFO, "Inicia sincronización de intervenciones", null);
        AssignmentService assignmentService = (AssignmentService) context.getBean("assignmentService");
        try {
            List<List<Object>> assignementExcelInfo = excelReader.read(excelPath, ExcelReader.ASSIGNMENT_SHEET_INDEX);
            LogHelper.Log(LogHelper.INFO, "Buscando asignaciones existentes en BD", null);
            List<Intervencion> assignmentInBD = assignmentService.findALl();
            //RelationMaps.setLanesInExcel(assignementExcelInfo);
            AssignmentComparator.compareList(assignementExcelInfo, assignmentInBD);
            LogHelper.Log(LogHelper.INFO, "Finaliza sincronización de intervenciones", null);
        }catch(FileNotFoundException fne) {
            String path = PropertiesHelper.getInstance().getProperties().getProperty("excel.filePath");
            LogHelper.Log(LogHelper.ERROR, "Error encontrando el archivo excel en la ruta " + path, fne);
        }catch(ExcelReaderException excelReaderException) {
            LogHelper.Log(LogHelper.ERROR, "Error en la lectura del archivo excel ", excelReaderException);
        }
    }
    
    
    
    public static ApplicationContext getContext() {
        return context;
    }
    
    public static String getDateFormatString() {
        return dateFormatString;
    }

    /**
     * Busca el id máximo en los tramos para asignar el id al nuevo tramo (El campo no es serial en BD)
     * @param lanesInBD
     * @return 
     */
    private static int findMaxId(List<TramoVia> lanesInBD) {
        int max = 0;
        for(TramoVia lane : lanesInBD)  {
           if(lane.getCodigoVia() > max)  {
               max = lane.getCodigoVia();
           }
        }
        max++;
        return max;
    }


    private static Map<String, String> readConcessions() throws IOException{
            LogHelper.Log(LogHelper.INFO, "Lectura de hoja de concesionarios", null);
            Map<String, String> concessionsMap = new HashMap<String, String>();
            try {
                List<List<Object>> concessionsExcelInfo = excelReader.read(excelPath, ExcelReader.CONCESSSIONS_SHEET_INDEX);
                for(List<Object> row : concessionsExcelInfo) {
                    if(row.get(0) != null && row.get(2) != null) {
                        String projectId = (String) row.get(0);
                        String concessionName = (String) row.get(2);
                        concessionsMap.put(projectId, concessionName);
                    }
                }
                return concessionsMap;
        }catch(FileNotFoundException fne) {
            String path = PropertiesHelper.getInstance().getProperties().getProperty("excel.filePath");
            LogHelper.Log(LogHelper.ERROR, "Error encontrando el archivo excel en la ruta " + path, fne);
        }catch(ExcelReaderException excelReaderException) {
            LogHelper.Log(LogHelper.ERROR, "Error en la lectura del archivo excel ", excelReaderException);
        }
        return null;
               
    }

   
}